package com.kang.c0c.optimize;

import java.util.HashSet;
import java.util.Iterator;

import com.kang.c0c.symbol.Symbol;
import com.kang.c0c.tree.node.BinaryNode;
import com.kang.c0c.tree.node.FunctionNode;
import com.kang.c0c.tree.node.Node;
import com.kang.c0c.tree.node.Nodes;
import com.kang.c0c.tree.node.SimpleBlock;

public class ActivityAnalyze {
	public ActivityAnalyze(){
		super();
	}
	
	public void analyze(FunctionNode funcNode){
		Node node = funcNode.getBlock().get(0);
		if(! (node instanceof SimpleBlock)){
			System.err.println("Error: ActivityAnalyzer.analyzeFunctionBlock");
			System.exit(-1);
		}
		analyzeBlock((SimpleBlock)node,
				new HashSet<Symbol>(),null,null);
	}
	
	private void analyzeBlock(SimpleBlock simple,
			HashSet<Symbol> nextIn,
			HashSet<Symbol> def,
			HashSet<Symbol> use){
		if(simple == null)
			return;

		simple.data.initDefUse();
		if(use != null){
			simple.data.mergeDefUse(def,use);
		}
		
		if(simple.next != null){
			SimpleBlock next = simple.next;
			analyzeBlock(next,nextIn,def,use);
			nextIn = next.data.getIn();
		}else{
			if(use != null){
				nextIn.addAll(use);
			}
		}
		
		if(simple.getBinary(simple.binarySize()-1).getNode() == Nodes.IF){
			analyzeIfBlock(simple, nextIn,def,use);			
		}else if(simple.getBinary(0).getNode() == Nodes.WHILE){
			analyzeWhileBlock(simple,nextIn,def,use);
		}else{			
			simple.data.addOut(nextIn);
			simple.data.computeIn();
		}
	}
	
	private void analyzeIfBlock(SimpleBlock simple,
			HashSet<Symbol> nextIn,
			HashSet<Symbol> def,
			HashSet<Symbol> use){
		HashSet<Symbol> def1 = null,
			def2 = null,
			use1 = null,
			use2 = null;
		BinaryNode binary =  (BinaryNode) simple.getBinary(simple.binarySize()-1).getRight();
		
		simple.data.addOut(nextIn);
		
		if(use != null){
			def1 = new HashSet<Symbol>(def);
			def2 = new HashSet<Symbol>(def);
			use1 = new HashSet<Symbol>(use);
			use2 = new HashSet<Symbol>(use);
		}
		
		if(binary.getLeft() != null){
			SimpleBlock left = (SimpleBlock)binary.getLeft();
			analyzeBlock(left,nextIn,def1,use1);
			simple.data.addOut(left.data.getIn());
		}
		
		if(binary.getRight() != null){
			SimpleBlock right = (SimpleBlock)binary.getRight();			
			analyzeBlock(right,nextIn,def2,use2);			
			simple.data.addOut(right.data.getIn());
		}
		
		if(use != null){
			def.addAll(intersection(def1,def2));
			use.addAll(use1);
			use.addAll(use2);			
		}
		
		simple.data.computeIn();
	}
	
	private void analyzeWhileBlock(SimpleBlock simple,
			HashSet<Symbol> nextIn,
			HashSet<Symbol> def,
			HashSet<Symbol> use){
		SimpleBlock execBlock = (SimpleBlock)simple.getBinary(0).getRight();
		
		if(use == null){
			use = new HashSet<Symbol>();
			def = new HashSet<Symbol>();
		}
		analyzeBlock(execBlock,nextIn,def,use);
		
		simple.data.addOut(nextIn);
		simple.data.addOut(execBlock.data.getIn());
		simple.data.computeIn();		
	}
	
	private HashSet<Symbol> intersection(HashSet<Symbol> set1, HashSet<Symbol> set2){
		HashSet<Symbol> set = new HashSet<Symbol>();
		Iterator<Symbol> syms = set1.iterator();
		
		while(syms.hasNext()){
			Symbol sym = syms.next();
			if(set2.contains(sym)){
				set.add(sym);
			}
		}
		
		return set;
	}
}